<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Libs\Plugins\QrCode;

use Exception;

class QrEncode
{
    private bool $eightBitMode              = false;
    private int $version                    = 0;
    public int $level                       = QR_EC_LEVEL_L;
    public int $hint                        = QR_MODE_8;

    public static function factory(int $level = QR_EC_LEVEL_L):self
    {
        $__class__          = static::class;
        $QrEncode           = new $__class__();
        $QrEncode->level    = in_array($level, [QR_EC_LEVEL_L, QR_EC_LEVEL_M, QR_EC_LEVEL_Q, QR_EC_LEVEL_H], true) ? $level : QR_EC_LEVEL_L;
        return $QrEncode;
    }

    private function encodeMask(QrInput $input, $mask): array
    {
        try{
            $raw    = new QrRawCode($input);
        }catch (Exception $Exception){
            return [];
        }
        $version    = $raw->version;
        $width      = QrSpec::getWidth($version);
        $frame      = QrSpec::newFrame($version);
        $filler     = new QrFrameFiller($width, $frame);
        for($i = 0; $i < $raw->dataLength + $raw->eccLength; $i++){
            $code   = $raw->getCode();
            $bit    = 0x80;
            for($j = 0; $j < 8; $j++){
                $addr   = $filler->next();
                $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0));
                $bit    = $bit >> 1;
            }
        }
        unset($raw);
        $j          = QrSpec::getRemainder($version);
        for($i = 0; $i < $j; $i++){
            $addr   = $filler->next();
            $filler->setFrameAt($addr, 0x02);
        }
        $frame      = $filler->frame;
        unset($filler);
        $maskObj    = new QrMask();
        if($mask < 0){
            return $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel());
        }else{
            return $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel());
        }
    }

    private function encodeRaw(string $text):array
    {
        if(strlen($text) < 1) return [];
        $QrInput    = new QrInput($this->version, $this->level);
        if($this->eightBitMode){
            $ret    = $QrInput->append(QR_MODE_8, strlen($text), str_split($text));
        }else{
            $ret    = QrSplit::splitStringToQrInput($text, $QrInput, $this->hint);
        }
        if($ret < 0) return [];
        return $this->encodeMask($QrInput, -1);
    }

    public function encode(string $text):array
    {
        $encodeData         = [];
        $encodeRawData      = $this->encodeRaw($text);
        if(count($encodeRawData) > 0) foreach ($encodeRawData as $encodeRawLine){
            $encodeRawStr   = '';
            foreach (str_split($encodeRawLine, 1) as $encodeRawChar) $encodeRawStr .= (ord($encodeRawChar)&1) ? '1' : '0';
            $encodeData[]   = $encodeRawStr;
        }
        return $encodeData;
    }
}